using System;
using System.Collections.Generic;
using System.Text;
using NetSpider.Retrieval;
using NetSpider.Retrieval.Policies;

namespace NetSpider.Retrieval
{
    public class Fetcher : IFetcher
    {
        private readonly IRetrieverFactory _retrieverFactory;
        private readonly IRetrievalPolicyFactory _retrievalPolicyFactory;

        private readonly List<RetrievalPolicy> _retrievalPolicies;
        private readonly List<IRetriever> _retrievers;

        private RetrievalPolicy _retrievalPolicy;

        public Fetcher(IRetrieverFactory retrieverFactory, IRetrievalPolicyFactory retrievalPolicyFactory)
        {
            _retrieverFactory = retrieverFactory;
            _retrievalPolicyFactory = retrievalPolicyFactory;

            _retrievalPolicies = new List<RetrievalPolicy>();
            _retrievers = new List<IRetriever>();
        }

        public RetrievalResult Fetch(FetchRequest item)
        {
            
            IRetriever retriever = GetRetrieverFor(item.UriSchema);
            _retrievalPolicy = GetRetrievalPolicyFor(item.UriSchema);

            RetrievalDecision decision = _retrievalPolicy.OnBeforeRetrieval(item.Uri);
            if (decision != RetrievalDecision.ContinueFetching)
            {
                //return new RetrievalResult(item, decision);
            }

            return (RetrievalResult) retriever.Retrieve(item.Uri, OnRetrievalProgress);
        }

        public IFetchResult Fetch(IFetchRequest item)
        {
            // Set things up for the retrieval.
            IRetriever retriever = GetRetrieverFor(item.RequestedUri.Scheme);
            _retrievalPolicy = GetRetrievalPolicyFor(item.RequestedUri.Scheme);

            // Check the retrieval policy before we start...
            RetrievalDecision decision = _retrievalPolicy.OnBeforeRetrieval(item.RequestedUri.ToString());

            if (decision == RetrievalDecision.ContinueFetching)
            {
                IRetrievalResult result = retriever.Retrieve(item.RequestedUri.ToString(), OnRetrievalProgress);
                return new FetchResult(item, result.GetContent());
            }

            return new FetchResult(item);
        }

        private IRetriever GetRetrieverFor(string uriSchema)
        {
            IRetriever retriever = null;
            foreach (IRetriever r in _retrievers)
            {
                foreach (string schema in r.GetSupportedUriSchemas())
                {
                    if (schema == uriSchema)
                    {
                        retriever = r;
                    }
                }
            }

            if (retriever == null)
            {
                retriever = _retrieverFactory.CreateFor(uriSchema);
            }

            return retriever;
        }

        private RetrievalPolicy GetRetrievalPolicyFor(string uriSchema)
        {
            RetrievalPolicy retrievalPolicy = null;
            foreach (RetrievalPolicy p in _retrievalPolicies)
            {
                foreach (string schema in p.GetSupportedUriSchemas())
                {
                    if (schema == uriSchema)
                    {
                        retrievalPolicy = p;
                    }
                }
            }

            if (retrievalPolicy == null)
            {
                retrievalPolicy = _retrievalPolicyFactory.CreateFor(uriSchema);
            }

            return retrievalPolicy;
        }

        public RetrievalDecision OnRetrievalProgress(long bytesRecieved)
        {
            return _retrievalPolicy.OnRetrievalProgress(bytesRecieved);
        }
    }

    public interface IFetchResult
    {
        bool Success { get; }
        IFetchRequest GetRequest();
        IContent GetContent();
    }
}